<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><div class="cl-preview-section"><h1 id="轻松上手provider（provider从入门到进阶）">轻松上手provider（provider从入门到进阶）</h1>
</div><div class="cl-preview-section"><p><img src="https://o.devio.org/images/fa/res/provider-so-easy.png" alt="provider-so-easy"></p>
</div><div class="cl-preview-section"><ul>
<li>轻松上手<code>provider</code></li>
<li><code>provider</code>进阶</li>
</ul>
</div><div class="cl-preview-section"><h2 id="轻松上手provider">轻松上手provider</h2>
</div><div class="cl-preview-section"><ol>
<li>安装<code>provider</code></li>
<li>定义供<code>provider</code>使用的数据模型</li>
<li>创建一个<code>provider</code></li>
<li>读取<code>provider</code>中的数据</li>
<li>改变<code>provider</code>中的数据</li>
</ol>
</div><div class="cl-preview-section"><h3 id="安装provider">安装<code>provider</code></h3>
</div><div class="cl-preview-section"><p>从<a href="https://pub.dev/packages/provider/install">https://pub.dev/packages/provider/install</a>上获取最新版本进行安装。</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart">dependencies<span class="token punctuation">:</span>
  provider<span class="token punctuation">:</span> xxx
</code></pre>
</div><div class="cl-preview-section"><h3 id="定义供provider使用的数据模型">定义供Provider使用的数据模型</h3>
</div><div class="cl-preview-section"><p>为了在Provider中进行数据共享首先我们需要为其定义一个数据模型，为了能够订阅数据的状态通常会让这个数据模型来继承<code>ChangeNotifier</code>。</p>
</div><div class="cl-preview-section"><blockquote>
<p>ChangeNotifier 是 Flutter SDK 中的一个简单的类。它用于向监听器发送通知。换言之，如果被定义为 ChangeNotifier，你可以订阅它的状态变化（这和大家所熟悉的观察者模式相类似）。</p>
</blockquote>
</div><div class="cl-preview-section"><p>在 provider 中，ChangeNotifier 是一种能够封装应用程序状态的方法。对于特别简单的程序，你可以通过一个 ChangeNotifier 来满足全部需求。在相对复杂的应用中，由于会有多个模型，所以可能会有多个 ChangeNotifier。</p>
</div><div class="cl-preview-section"><p>示例：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart"><span class="token keyword">class</span> <span class="token class-name">Counter</span> <span class="token keyword">with</span> ChangeNotifier <span class="token punctuation">{</span>
  int _count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

  int <span class="token keyword">get</span> count <span class="token operator">=</span><span class="token operator">&gt;</span> _count<span class="token punctuation">;</span>

  <span class="token keyword">void</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    _count<span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token function">notifyListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p>在上述代码中我们定义了一个计数器的数据模型，通过<code>increment()</code>方法来改变<code>_count</code>，通过<code>get count</code>来暴露<code>_count</code>的值。</p>
</div><div class="cl-preview-section"><h3 id="创建一个provider">创建一个Provider</h3>
</div><div class="cl-preview-section"><p>在创建Provider之前我们首先来了解下provider中提供的几种不同类型的"provider"：</p>
</div><div class="cl-preview-section"><div class="table-wrapper"><table>
<thead>
<tr>
<th>名称</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Provider</td>
<td>最基础的provider组成，接收一个值并暴露它， 无论值是什么。（<strong>通常不直接使用Provider</strong>）</td>
</tr>
<tr>
<td>ListenableProvider</td>
<td>供可监听对象使用的特殊provider，ListenableProvider会监听对象，并在监听器被调用时更新依赖此对象的widgets。</td>
</tr>
<tr>
<td>ChangeNotifierProvider</td>
<td>为ChangeNotifier提供的ListenableProvider规范，会在需要时自动调用ChangeNotifier.dispose。（<strong>通常用来代替ListenableProvider来使用</strong>）</td>
</tr>
<tr>
<td>ValueListenableProvider</td>
<td>监听ValueListenable，并且只暴露出ValueListenable.value。</td>
</tr>
<tr>
<td>StreamProvider</td>
<td>监听流，并暴露出当前的最新值。</td>
</tr>
<tr>
<td>FutureProvider</td>
<td>接收一个Future，并在其进入complete状态时更新依赖它的组件。</td>
</tr>
</tbody>
</table>
</div></div><div class="cl-preview-section"><p>示例：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart"><span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">runApp</span><span class="token punctuation">(</span>
    <span class="token function">ChangeNotifierProvider</span><span class="token punctuation">(</span>
      create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">Counter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      child<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">MyApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="读取provider中的数据">读取Provider中的数据</h3>
</div><div class="cl-preview-section"><p>读取Provider中的数据可以通过 <code>context.watch</code> 、 <code>context.read</code>、<code>context.select</code> 来实现：</p>
</div><div class="cl-preview-section"><ul>
<li><code>context.watch&lt;T&gt;()</code>， 一方法使得widget能够监听泛型<code>T</code>上发生的改变。</li>
<li><code>context.read&lt;T&gt;()</code>，直接返回<code>T</code>，不会监听改变。</li>
<li><code>context.select&lt;T， R&gt;(R cb(T value))</code>，允许widget只监听<code>T</code>上的一部分(<code>R</code>)。</li>
</ul>
</div><div class="cl-preview-section"><p>需要注意的是，<code>context.read&lt;T&gt;()</code> 方法不会在值变化时重新widget， <strong>并且不能在 <code>StatelessWidget.build</code>/<code>State.build</code> 方法内调用</strong>，如果要要在build中访问provider中的数据可以用  <code>context.watch&lt;T&gt;()</code>代替。</p>
</div><div class="cl-preview-section"><blockquote>
<p><code>context.xxx</code>是provider 4.1.0中给<code>BuildContext</code>添加的拓展方法，等价于<code>Provider.of&lt;T&gt;(context)</code>。</p>
</blockquote>
</div><div class="cl-preview-section"><ul>
<li><code>Provider.of&lt;T&gt;(context)</code>类似  <code>context.watch&lt;T&gt;()</code>；</li>
<li><code>Provider.of&lt;T&gt;(context，listen: false)</code>类似于 <code>context.read&lt;T&gt;()</code>；</li>
</ul>
</div><div class="cl-preview-section"><p>示例：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart"><span class="token keyword">class</span> <span class="token class-name">Count</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token function">Count</span><span class="token punctuation">(</span><span class="token punctuation">{</span>Key key<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">Text</span><span class="token punctuation">(</span>
      <span class="token comment">///使用 `context.watch` 可以在`Counter`改变之后重新构建Count</span>
        <span class="token string">'${context.watch&lt;Counter&gt;().count}'</span><span class="token punctuation">,</span>
        key<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Key</span><span class="token punctuation">(</span><span class="token string">'counterState'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        style<span class="token punctuation">:</span> Theme<span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">.</span>textTheme<span class="token punctuation">.</span>headline4<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="改变provider中的数据">改变Provider中的数据</h3>
</div><div class="cl-preview-section"><p>要改变改变Provider中的数据只需要获取到Provider中对应的模型然后调用模型的对应方法就可以了：</p>
</div><div class="cl-preview-section"><p>示例：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart"><span class="token keyword">class</span> <span class="token class-name">MyHomePage</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token function">MyHomePage</span><span class="token punctuation">(</span><span class="token punctuation">{</span>Key key<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">Scaffold</span><span class="token punctuation">(</span>
      appBar<span class="token punctuation">:</span> <span class="token function">AppBar</span><span class="token punctuation">(</span>
        title<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'轻松上手provider'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
      body<span class="token punctuation">:</span> <span class="token function">Center</span><span class="token punctuation">(</span>
        child<span class="token punctuation">:</span> <span class="token function">Column</span><span class="token punctuation">(</span>
          mainAxisSize<span class="token punctuation">:</span> MainAxisSize<span class="token punctuation">.</span>min<span class="token punctuation">,</span>
          mainAxisAlignment<span class="token punctuation">:</span> MainAxisAlignment<span class="token punctuation">.</span>center<span class="token punctuation">,</span>
          children<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token operator">&lt;</span>Widget<span class="token operator">&gt;</span><span class="token punctuation">[</span>
            <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'点击按钮看看效果。'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

            <span class="token comment">/// 性能优化：这里将会频繁触发build的操作封装到单独的widget中来避免对整个页面频繁的build</span>
            <span class="token function">Count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
          <span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
      floatingActionButton<span class="token punctuation">:</span> <span class="token function">FloatingActionButton</span><span class="token punctuation">(</span>
        key<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Key</span><span class="token punctuation">(</span><span class="token string">'点我'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

        <span class="token comment">///性能优化： 在这里调用`context.read`而不是`context.watch`，主要是为了避免当Counter变化时而带来的rebuild</span>
        onPressed<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> context<span class="token punctuation">.</span>read<span class="token operator">&lt;</span>Counter<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        tooltip<span class="token punctuation">:</span> <span class="token string">'+1'</span><span class="token punctuation">,</span>
        child<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Icon</span><span class="token punctuation">(</span>Icons<span class="token punctuation">.</span>add<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p>在上述代码中我们调用了<code>Counter</code>数据模型的<code>increment()</code>方法，此时我们不妨回想下该方法中的具体实现：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart"> <span class="token keyword">void</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    _count<span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token function">notifyListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p>在这个方法中先是将<code>_count</code>的值加1，然后通过<code>notifyListeners()</code> API来通知读取了<code>_count</code>值的地方进行更新（很典型的观察者模式）。</p>
</div><div class="cl-preview-section"><p>到这里呢，我们变从0到1使用了provider来实现了对数据<code>_count</code>的操作和共享。下面贴出示例的完成代码，小伙伴们可以将其复制到项目的main.dart中来运行下查看效果：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart"><span class="token keyword">import</span> <span class="token string">'package:flutter/foundation.dart'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token string">'package:flutter/material.dart'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token string">'package:provider/provider.dart'</span><span class="token punctuation">;</span>

<span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">runApp</span><span class="token punctuation">(</span>
    <span class="token function">ChangeNotifierProvider</span><span class="token punctuation">(</span>
      create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">Counter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      child<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">MyApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Counter</span> <span class="token keyword">with</span> ChangeNotifier <span class="token punctuation">{</span>
  int _count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

  int <span class="token keyword">get</span> count <span class="token operator">=</span><span class="token operator">&gt;</span> _count<span class="token punctuation">;</span>

  <span class="token keyword">void</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    _count<span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token function">notifyListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">MyApp</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token function">MyApp</span><span class="token punctuation">(</span><span class="token punctuation">{</span>Key key<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">const</span> <span class="token function">MaterialApp</span><span class="token punctuation">(</span>
      home<span class="token punctuation">:</span> <span class="token function">MyHomePage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">MyHomePage</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token function">MyHomePage</span><span class="token punctuation">(</span><span class="token punctuation">{</span>Key key<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">Scaffold</span><span class="token punctuation">(</span>
      appBar<span class="token punctuation">:</span> <span class="token function">AppBar</span><span class="token punctuation">(</span>
        title<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'轻松上手provider'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
      body<span class="token punctuation">:</span> <span class="token function">Center</span><span class="token punctuation">(</span>
        child<span class="token punctuation">:</span> <span class="token function">Column</span><span class="token punctuation">(</span>
          mainAxisSize<span class="token punctuation">:</span> MainAxisSize<span class="token punctuation">.</span>min<span class="token punctuation">,</span>
          mainAxisAlignment<span class="token punctuation">:</span> MainAxisAlignment<span class="token punctuation">.</span>center<span class="token punctuation">,</span>
          children<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token operator">&lt;</span>Widget<span class="token operator">&gt;</span><span class="token punctuation">[</span>
            <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'点击按钮看看效果。'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

            <span class="token comment">/// 性能优化：这里将会频繁触发build的操作封装到单独的widget中来避免对整个页面频繁的build</span>
            <span class="token function">Count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
          <span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
      floatingActionButton<span class="token punctuation">:</span> <span class="token function">FloatingActionButton</span><span class="token punctuation">(</span>
        key<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Key</span><span class="token punctuation">(</span><span class="token string">'点我'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

        <span class="token comment">///性能优化： 在这里调用`context.read`而不是`context.watch`，主要是为了避免当Counter变化时而带来的rebuild</span>
        onPressed<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> context<span class="token punctuation">.</span>read<span class="token operator">&lt;</span>Counter<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        tooltip<span class="token punctuation">:</span> <span class="token string">'+1'</span><span class="token punctuation">,</span>
        child<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Icon</span><span class="token punctuation">(</span>Icons<span class="token punctuation">.</span>add<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Count</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token function">Count</span><span class="token punctuation">(</span><span class="token punctuation">{</span>Key key<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">Text</span><span class="token punctuation">(</span>
        <span class="token comment">///使用 `context.watch` 可以在`Counter`改变之后重新构建Count</span>
        <span class="token string">'${context.watch&lt;Counter&gt;().count}'</span><span class="token punctuation">,</span>
        key<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Key</span><span class="token punctuation">(</span><span class="token string">'counterState'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        style<span class="token punctuation">:</span> Theme<span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">.</span>textTheme<span class="token punctuation">.</span>headline4<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p>上手了<code>provider</code>之后呢，我们接下来来进一步学习<code>provider</code>中一些更高级的概念和功能：</p>
</div><div class="cl-preview-section"><h2 id="provider进阶"><code>provider</code>进阶</h2>
</div><div class="cl-preview-section"><p>接下来我们来学习下<code>provider</code>中的一些更高级的概念和功能：</p>
</div><div class="cl-preview-section"><ul>
<li>ProxyProvider</li>
<li>MultiProvider</li>
<li>Consumer</li>
</ul>
</div><div class="cl-preview-section"><h3 id="proxyprovider">ProxyProvider</h3>
</div><div class="cl-preview-section"><p>从3.0.0开始， provider中新增了一种新的provider: <code>ProxyProvider</code>：</p>
</div><div class="cl-preview-section"><p><code>ProxyProvider</code>能够将多个来自于其他的providers的值聚合为一个新对象，并且将结果传递给<code>Provider</code>。</p>
</div><div class="cl-preview-section"><p>这个新对象会在其依赖的任一providers更新后被更新</p>
</div><div class="cl-preview-section"><p>下面的例子使用<code>ProxyProvider</code>，基于来自于另一个provider的counter值进行转化。</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart">Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">MultiProvider</span><span class="token punctuation">(</span>
    providers<span class="token punctuation">:</span> <span class="token punctuation">[</span>
      <span class="token function">ChangeNotifierProvider</span><span class="token punctuation">(</span>create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">Counter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>，
      ProxyProvider<span class="token operator">&lt;</span>Counter， Translations<span class="token operator">&gt;</span><span class="token punctuation">(</span>
        update<span class="token punctuation">:</span> <span class="token punctuation">(</span>_， counter， __<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">Translations</span><span class="token punctuation">(</span>counter<span class="token punctuation">.</span>value<span class="token punctuation">)</span>，
      <span class="token punctuation">)</span>，
    <span class="token punctuation">]</span>，
    child<span class="token punctuation">:</span> <span class="token function">Foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span>，
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Translations</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token function">Translations</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>_value<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">final</span> int _value<span class="token punctuation">;</span>

  String <span class="token keyword">get</span> title <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token string">'You clicked $_value times'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p>这个例子还有多种变化:</p>
</div><div class="cl-preview-section"><ul>
<li>
<p><code>ProxyProvider</code> vs <code>ProxyProvider2</code> vs <code>ProxyProvider3</code>， …</p>
<p>类名后的数字是 <code>ProxyProvider</code> 依赖的其他providers的数量</p>
</li>
<li>
<p><code>ProxyProvider</code> vs <code>ChangeNotifierProxyProvider</code> vs <code>ListenableProxyProvider</code>， …</p>
<p>它们工作的方式是相似的， 但 <code>ChangeNotifierProxyProvider</code> 会将它的值传递给<code>ChangeNotifierProvider</code> 而非 <code>Provider</code>。</p>
</li>
</ul>
</div><div class="cl-preview-section"><p>在provider中不允许以可能随时间改变的变量来创建对象比如：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart">int count<span class="token punctuation">;</span>

<span class="token function">Provider</span><span class="token punctuation">(</span>
  create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">MyModel</span><span class="token punctuation">(</span>count<span class="token punctuation">)</span>，
  child<span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token punctuation">)</span>
</code></pre>
</div><div class="cl-preview-section"><p>上述代码是一个反面❌案例，在这种情况下，即使<code>count</code>变量发生变化，那么<code>MyModel</code>对象也不会更新。如果<code>MyModel</code>需要依赖一个变量来创建那么正确✅的做法是使用ProxyProvider：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart">int count<span class="token punctuation">;</span>

<span class="token function">ProxyProvider0</span><span class="token punctuation">(</span>
  update<span class="token punctuation">:</span> <span class="token punctuation">(</span>_， __<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">MyModel</span><span class="token punctuation">(</span>count<span class="token punctuation">)</span>，
  child<span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token punctuation">)</span>
</code></pre>
</div><div class="cl-preview-section"><p>因为在这个案例中MyModel的创建只依赖一个变量而不依赖自于其他的providers的值，所以我们需要使用<code>ProxyProvider0</code>。</p>
</div><div class="cl-preview-section"><blockquote>
<p>注意：在使用一个provider的create/update回调时，回调函数默认是懒调用的。也就是说， 除非这个值被读取了至少一次， 否则create/update函数不会被调用。</p>
</blockquote>
</div><div class="cl-preview-section"><p>如果你想预先计算一些逻辑， 可以通过使用lazy参数来禁用这一行为。</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart"><span class="token function">MyProvider</span><span class="token punctuation">(</span>
  create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">Something</span><span class="token punctuation">(</span><span class="token punctuation">)</span>，
  lazy<span class="token punctuation">:</span> <span class="token boolean">false</span>，
<span class="token punctuation">)</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="multiprovider">MultiProvider</h3>
</div><div class="cl-preview-section"><p>当在大型应用中注入较多状态时， <code>Provider</code> 很容易变得高度耦合:</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart">Provider<span class="token operator">&lt;</span>Something<span class="token operator">&gt;</span><span class="token punctuation">(</span>
  create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">Something</span><span class="token punctuation">(</span><span class="token punctuation">)</span>，
  child<span class="token punctuation">:</span> Provider<span class="token operator">&lt;</span>SomethingElse<span class="token operator">&gt;</span><span class="token punctuation">(</span>
    create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">SomethingElse</span><span class="token punctuation">(</span><span class="token punctuation">)</span>，
    child<span class="token punctuation">:</span> Provider<span class="token operator">&lt;</span>AnotherThing<span class="token operator">&gt;</span><span class="token punctuation">(</span>
      create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">AnotherThing</span><span class="token punctuation">(</span><span class="token punctuation">)</span>，
      child<span class="token punctuation">:</span> someWidget，
    <span class="token punctuation">)</span>，
  <span class="token punctuation">)</span>，
<span class="token punctuation">)</span>，
</code></pre>
</div><div class="cl-preview-section"><p>上述代码是一个反面❌案例，在这种情况下，正确✅的做法是使用<code>MultiProvider</code>:</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart"><span class="token function">MultiProvider</span><span class="token punctuation">(</span>
  providers<span class="token punctuation">:</span> <span class="token punctuation">[</span>
    Provider<span class="token operator">&lt;</span>Something<span class="token operator">&gt;</span><span class="token punctuation">(</span>create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">Something</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>，
    Provider<span class="token operator">&lt;</span>SomethingElse<span class="token operator">&gt;</span><span class="token punctuation">(</span>create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">SomethingElse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>，
    Provider<span class="token operator">&lt;</span>AnotherThing<span class="token operator">&gt;</span><span class="token punctuation">(</span>create<span class="token punctuation">:</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">AnotherThing</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>，
  <span class="token punctuation">]</span>，
  child<span class="token punctuation">:</span> someWidget，
<span class="token punctuation">)</span>
</code></pre>
</div><div class="cl-preview-section"><p>以上两个例子的实际表现是一致的， <code>MultiProvider</code>唯一改变的就是代码书写方式。</p>
</div><div class="cl-preview-section"><h3 id="consumer">Consumer</h3>
</div><div class="cl-preview-section"><p>在<code>provider</code>中有另外一个非常重要的概念叫Consumer，它主要有两个作用：</p>
</div><div class="cl-preview-section"><ul>
<li>提供数据读取：用来代替<code>Provider.of&lt;T&gt;(context)</code>来获取<code>provider</code>中的数据；</li>
<li>优化性能：提供更加精细的数据刷新范围，避免无谓的刷新；</li>
</ul>
</div><div class="cl-preview-section"><p>首先我们先来说说Consumer的第一个作用：</p>
</div><div class="cl-preview-section"><h4 id="提供数据读取">提供数据读取</h4>
</div><div class="cl-preview-section"><p>接下来我们使用<code>Consumer</code>来对上文中的案例进行改造：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart"><span class="token keyword">class</span> <span class="token class-name">MyHomePage</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token function">MyHomePage</span><span class="token punctuation">(</span><span class="token punctuation">{</span>Key key<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">Scaffold</span><span class="token punctuation">(</span>
        appBar<span class="token punctuation">:</span> <span class="token function">AppBar</span><span class="token punctuation">(</span>
          title<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'轻松上手provider'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">,</span>
        body<span class="token punctuation">:</span> Consumer<span class="token operator">&lt;</span>Counter<span class="token operator">&gt;</span><span class="token punctuation">(</span>
            builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">,</span> Counter counter<span class="token punctuation">,</span> child<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">Center</span><span class="token punctuation">(</span>
                  child<span class="token punctuation">:</span> <span class="token function">Column</span><span class="token punctuation">(</span>
                    mainAxisSize<span class="token punctuation">:</span> MainAxisSize<span class="token punctuation">.</span>min<span class="token punctuation">,</span>
                    mainAxisAlignment<span class="token punctuation">:</span> MainAxisAlignment<span class="token punctuation">.</span>center<span class="token punctuation">,</span>
                    children<span class="token punctuation">:</span> <span class="token operator">&lt;</span>Widget<span class="token operator">&gt;</span><span class="token punctuation">[</span>
                      <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'点击按钮看看效果。'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                      <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'${counter.count}'</span><span class="token punctuation">)</span>
                    <span class="token punctuation">]</span><span class="token punctuation">,</span>
                  <span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        floatingActionButton<span class="token punctuation">:</span> Consumer<span class="token operator">&lt;</span>Counter<span class="token operator">&gt;</span><span class="token punctuation">(</span>
            builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">,</span> Counter counter<span class="token punctuation">,</span> child<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">FloatingActionButton</span><span class="token punctuation">(</span>
                  key<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Key</span><span class="token punctuation">(</span><span class="token string">'点我'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                  onPressed<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> counter<span class="token punctuation">.</span><span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                  tooltip<span class="token punctuation">:</span> <span class="token string">'+1'</span><span class="token punctuation">,</span>
                  child<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Icon</span><span class="token punctuation">(</span>Icons<span class="token punctuation">.</span>add<span class="token punctuation">)</span><span class="token punctuation">,</span>
                <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p>在上述代码中我们使用了Consumer代替了<code>context.read</code>和<code>context.watch</code>来获取数据。另外，观察上述代码不难发现：</p>
</div><div class="cl-preview-section"><p>Consumer 使用了 Builder 模式，当它的泛型变化时就会通过 builder 重新构建。Consumer 代表了它要获取哪一个祖先中的 数据模型。</p>
</div><div class="cl-preview-section"><p>Consumer 的 builder 实际上就是一个 Function，它接收三个参数<code>(BuildContext context, T model, Widget child)</code>：</p>
</div><div class="cl-preview-section"><ul>
<li>context：build 方法传进来的 BuildContext；</li>
<li>T：获取到的最近一个祖先节点中的数据模型；</li>
<li>child：它用来构建那些与provider中数据模型无关的部分，在多次运行 builder 中，child 不会进行重建；</li>
</ul>
</div><div class="cl-preview-section"><p>上述案例中，当<code>Counter</code>每次变化都会导致builder方法调用，从而导致其里面的widget被重新创建。</p>
</div><div class="cl-preview-section"><p>那么，接下来我们继续探索下如何使用Consumer进行性能优化。</p>
</div><div class="cl-preview-section"><h3 id="优化性能">优化性能</h3>
</div><div class="cl-preview-section"><p>下面我们借助Consumer 的builder中的child 不会进行重建的原理来对上述案例进行优化：</p>
</div><div class="cl-preview-section"><pre class=" language-dart"><code class="prism  language-dart">  <span class="token metadata symbol">@override</span>
  Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">Scaffold</span><span class="token punctuation">(</span>
        appBar<span class="token punctuation">:</span> <span class="token function">AppBar</span><span class="token punctuation">(</span>
          title<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'轻松上手provider'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">,</span>
        body<span class="token punctuation">:</span> Consumer<span class="token operator">&lt;</span>Counter<span class="token operator">&gt;</span><span class="token punctuation">(</span>
          builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">,</span> Counter counter<span class="token punctuation">,</span> child<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">Center</span><span class="token punctuation">(</span>
            child<span class="token punctuation">:</span> <span class="token function">Column</span><span class="token punctuation">(</span>
              mainAxisSize<span class="token punctuation">:</span> MainAxisSize<span class="token punctuation">.</span>min<span class="token punctuation">,</span>
              mainAxisAlignment<span class="token punctuation">:</span> MainAxisAlignment<span class="token punctuation">.</span>center<span class="token punctuation">,</span>
              children<span class="token punctuation">:</span> <span class="token operator">&lt;</span>Widget<span class="token operator">&gt;</span><span class="token punctuation">[</span>child<span class="token punctuation">,</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'${counter.count}'</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
            <span class="token punctuation">)</span><span class="token punctuation">,</span>
          <span class="token punctuation">)</span><span class="token punctuation">,</span>
          child<span class="token punctuation">:</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'点击按钮看看效果。'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">,</span>
        floatingActionButton<span class="token punctuation">:</span> Consumer<span class="token operator">&lt;</span>Counter<span class="token operator">&gt;</span><span class="token punctuation">(</span>
            builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">,</span> Counter counter<span class="token punctuation">,</span> child<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">FloatingActionButton</span><span class="token punctuation">(</span>
                  key<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Key</span><span class="token punctuation">(</span><span class="token string">'点我'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                  onPressed<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> counter<span class="token punctuation">.</span><span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                  tooltip<span class="token punctuation">:</span> <span class="token string">'+1'</span><span class="token punctuation">,</span>
                  child<span class="token punctuation">:</span> child<span class="token punctuation">,</span>
                <span class="token punctuation">)</span><span class="token punctuation">,</span>
            child<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token function">Icon</span><span class="token punctuation">(</span>Icons<span class="token punctuation">.</span>add<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p>在上述代码中我们将不依赖数据模型而变化的widget提取到child中，这样当<code>Counter</code>变化后被提取到child中的widget就不会被重新创建，从而达到了性能优化的目的。</p>
</div><div class="cl-preview-section"><h2 id="最后">最后</h2>
</div><div class="cl-preview-section"><p>另外，<a href="https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md"><code>provider</code>官方文档</a>上对各个API有比较细的说明，强烈建议小伙伴们读一读对你提升对<code>provider</code>的认知和理解会有比较大的帮助。</p>
</div> <h1 style="color: red">认准一手，获取完整资料<br>QQ3195303913<br>微信wxywd8<br><br><br><br>高价回收正版课</h1></body></html>